package cn.charlab.boot.s3;

import cn.charlab.boot.exception.OsException;
import cn.charlab.boot.template.StorageTemplate;
import cn.charlab.boot.s3.properties.S3ConfigProperties;
import com.amazonaws.ClientConfiguration;
import com.amazonaws.Protocol;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.S3ClientOptions;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.S3Object;
import org.apache.commons.io.IOUtils;
import org.springframework.util.StringUtils;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;

public class S3Template implements StorageTemplate {

    private S3ConfigProperties s3ConfigProperties;

    private AmazonS3 amazonS3;

    public S3Template(S3ConfigProperties s3ConfigProperties){
        this.s3ConfigProperties = s3ConfigProperties;
    }

    @Override
    public boolean createBucket(String bucket) throws OsException {
        if (StringUtils.hasLength(bucket)) {
            return false;
        }
        if (bucketExists(bucket)) {
            return false;
        }
        try {

            amazonS3.createBucket(bucket);
            return true;
        } catch (Exception e) {
            throw new OsException(e);
        }
    }

    @Override
    public boolean deleteBucket(String bucket) throws OsException {
        try {
            amazonS3.deleteBucket(bucket);
        } catch (Exception e) {
            throw new OsException(e);
        }
        return true;
    }

    @Override
    public boolean bucketExists(String bucket) throws OsException {
        try {

            return amazonS3.doesBucketExist(bucket);
        } catch (Exception e) {
            throw new OsException(e);
        }
    }

    @Override
    public void putObject(String bucket, String key, InputStream input) throws OsException {
        putObject(bucket, key, input, null);
    }

    @Override
    public void putObject(String bucket, String key, InputStream input,
                          Map<String, Object> headers) throws OsException {
        try {
            // 创建文件上传的元数据
            ObjectMetadata meta = new ObjectMetadata();
            // 设置文件上传长度
            meta.setContentLength(input.available());
            if (headers != null) {
                headers.forEach((k, v) ->  meta.setHeader(k, v));
            }
            // 上传
            amazonS3.putObject(bucket, key, input, meta);
        } catch (Exception e) {
            throw new OsException(e);
        }
    }

    @Override
    public InputStream getObject(String bucket, String key) throws OsException {
        try {
            S3Object o = amazonS3.getObject(bucket, key);
            return o.getObjectContent();
        } catch (Exception e) {
            throw new OsException(e);
        }
    }

    @Override
    public void getObject(String bucket, String key, OutputStream out) throws OsException {
        S3Object o = amazonS3.getObject(bucket, key);
        try (InputStream in = o.getObjectContent()) {
            IOUtils.copyLarge(in, out);
        } catch (Exception e) {
            throw new OsException(e);
        }
    }

    @Override
    public boolean deleteObject(String bucket, String key) throws OsException {
        try {
            amazonS3.deleteObject(bucket, key);
        } catch (Exception e) {
            throw new OsException(e);
        }
        return true;
    }

    @Override
    public boolean existObject(String bucket, String key) throws OsException {
        try {
            return amazonS3.doesObjectExist(bucket, key);
        } catch (Exception e) {
            throw new OsException(e);
        }
    }

    @Override
    public void destroy() throws Exception {
        amazonS3.shutdown();
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        init();
    }




    private void init() throws MalformedURLException {
        if (!s3Enable()){
            return;
        }
        URL endpointUrl = new URL(s3ConfigProperties.getEndPoint());
        String protocol = endpointUrl.getProtocol();
        int port = endpointUrl.getPort() == -1
                ? endpointUrl.getDefaultPort() : endpointUrl.getPort();
        ClientConfiguration clientConfig = new ClientConfiguration();
        clientConfig.setSignerOverride("S3SignerType");
        clientConfig.setProtocol(Protocol.valueOf(protocol.toUpperCase()));
        // 禁用证书检查，避免https自签证书校验失败
        System.setProperty("com.amazonaws.sdk.disableCertChecking", "true");
        // 屏蔽 AWS 的 MD5 校验，避免校验导致的下载抛出异常问题
        System.setProperty("com.amazonaws.services.s3.disableGetObjectMD5Validation", "true");
        AWSCredentials awsCredentials =
                new BasicAWSCredentials(s3ConfigProperties.getAccessKey(), s3ConfigProperties.getSecretKey());
        // 创建 S3Client 实例
        amazonS3 = new AmazonS3Client(awsCredentials, clientConfig);
        amazonS3.setEndpoint(endpointUrl.getHost() + ":" + port);
        amazonS3.setS3ClientOptions(S3ClientOptions.builder().setPathStyleAccess(true).build());
    }

    private boolean s3Enable(){
        if (s3ConfigProperties == null || !StringUtils.hasLength(s3ConfigProperties.getEndPoint())){
            return false;
        }
        return true;
    }

}
